class cdiff2 { // diff ^ 2
  // ========== constructor ========== //
  cdiff2(int rawData[], int pattern[]) {
    this.rawData = rawData;
    LEN_RAW_DATA = rawData.length;

    this.pattern = pattern;
    LEN_PATTERN = pattern.length;

    LEN_RSLT = LEN_RAW_DATA - LEN_PATTERN;

    rsltVal = new double[LEN_RSLT];
    rsltHue = new int[LEN_RSLT];
    rsltMatchingRate = new int[LEN_RSLT];

    layer = createGraphics(width, 600);

    println("LEN_RAW_DATA = " + LEN_RAW_DATA);
    println("LEN_PATTERN = " + LEN_PATTERN);
    println("LEN_RSLT = " + LEN_RSLT);
  }

  // ========== calculate and visualize ========== //
  void calculate(String mode) {
    int raw_i, pattern_i;
    double value;
    this.mode = mode;

    maxVal = 0;
    minVal = 3.40282347E+38;

    for (raw_i = 0; raw_i < LEN_RSLT; raw_i++) {
      value = 0.0;
      for (pattern_i = 0; pattern_i < LEN_PATTERN; pattern_i++) {
        int i = raw_i + pattern_i;
        int r = rawData[i];
        int p = pattern[pattern_i];

        if (mode == "square") {
          value += pow(r - p, 2); // this is so-called: diff^2. the difference between raw and pattern, then power by square. --> (r - p) ^ 2
        } else if (mode == "abs") {
          value += abs(r - p);
        } else {
          println("\n\n!!! WRONG MODE !!!");
          exit();
        }
      }
      // value = value / LEN_PATTERN; // no need to use average, use sumation is also OK

      // update min and max
      if (maxVal < value ) {
        maxVal = value;
      }
      if (minVal > value) {
        minVal = value;
      }

      // store result of this round
      rsltVal[raw_i] = value;
    }
    println("\npattern diff^2 with raw data finished. min value = " + minVal + ", max value = " + maxVal);
    println("max value " + maxVal + " and 0 are mapped between 'most unlikely (0%)' and 'most likely (100%)'");
    println("max matching rate found in the wavform = " + (int)map((float)minVal, 0, (float)maxVal, 100, 0) + "%");

    printResult();
    visualize();
  }

  // ========== show the vusualing result ========== //
  void show() {
    image(layer, 0, 300);
  }

  // ========== get mode ========== //
  String getMode() {
    return mode;
  }

  // ========== printResult ========== //
  private void printResult() {
    PrintWriter log_val;
    // you must make sure value.log is accessible
    log_val = createWriter("value.log");

    for (int i = 0; i < LEN_RSLT; i++) {
      log_val.println(rsltVal[i]);
    }
    log_val.flush();
    log_val.close();
  }

  // ========== visualize ========== //
  private void visualize() {
    PrintWriter log_mr;
    // you must make sure value.log is accessible
    log_mr = createWriter("matching_rate.log");

    layer.beginDraw();
    layer.background(#EEEEEE);
    layer.colorMode(HSB, 360, 100, 100);

    int xpos;

    for (int i = 0; i < LEN_RSLT; i++) {
      xpos = (int)map(i, 0, LEN_RAW_DATA, 0, width);
      float val = (float)rsltVal[i];

      // map value to other parameters, use 0 as bottom, not minVal
      int len = (int)map(val, 0, (float)maxVal, layer.height, 0);
      int hue = (int)map(val, 0, (float)maxVal, 0, 150);
      int mr = (int)map(val, 0, (float)maxVal, 100, 0); // result [0, maxVal] map to matching rate [100%, 0]

      log_mr.println(mr);
      rsltMatchingRate[i] = mr;
      rsltHue[i] = hue;

      layer.fill(hue, 100, 100); // HSB range: H 360, S 100, B 100
      layer.stroke(hue, 100, 100);
      layer.rect(xpos, 0, 1, len);
    }

    layer.endDraw();
    log_mr.flush();
    log_mr.close();
  }

  // ========== getHue ========== //
  int getHue(int pos) {
    return rsltHue[pos];
  }

  // ========== get matching rate ========== //
  int getMatchingRate(int pos) {
    return rsltMatchingRate[pos];
  }

  // ========== member var ========== //
  private int rawData[];
  private int pattern[];

  private final int LEN_RAW_DATA;
  private final int LEN_PATTERN;
  private final int LEN_RSLT;

  private double rsltVal[];
  private int rsltHue[];
  private int rsltMatchingRate[];

  private double maxVal;
  private double minVal;

  private PGraphics layer;

  private String mode;
};
